home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2007 September / PCWSEP07.iso / Software / Linux / Linux Mint 3.0 Light / LinuxMint-3.0-Light.iso / casper / filesystem.squashfs / usr / lib / sunbird / js / calItemBase.js < prev    next >
Encoding:
Text File  |  2007-05-23  |  25.6 KB  |  784 lines

  1. /* -*- Mode: javascript; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2. /* ***** BEGIN LICENSE BLOCK *****
  3.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4.  *
  5.  * The contents of this file are subject to the Mozilla Public License Version
  6.  * 1.1 (the "License"); you may not use this file except in compliance with
  7.  * the License. You may obtain a copy of the License at
  8.  * http://www.mozilla.org/MPL/
  9.  *
  10.  * Software distributed under the License is distributed on an "AS IS" basis,
  11.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12.  * for the specific language governing rights and limitations under the
  13.  * License.
  14.  *
  15.  * The Original Code is Oracle Corporation code.
  16.  *
  17.  * The Initial Developer of the Original Code is
  18.  *  Oracle Corporation
  19.  * Portions created by the Initial Developer are Copyright (C) 2004
  20.  * the Initial Developer. All Rights Reserved.
  21.  *
  22.  * Contributor(s):
  23.  *   Vladimir Vukicevic <vladimir.vukicevic@oracle.com>
  24.  *   Mike Shaver <shaver@off.net>
  25.  *   Joey Minta <jminta@gmail.com>
  26.  *   Matthew Willis <lilmatt@mozilla.com>
  27.  *
  28.  * Alternatively, the contents of this file may be used under the terms of
  29.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  30.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  31.  * in which case the provisions of the GPL or the LGPL are applicable instead
  32.  * of those above. If you wish to allow use of your version of this file only
  33.  * under the terms of either the GPL or the LGPL, and not to allow others to
  34.  * use your version of this file under the terms of the MPL, indicate your
  35.  * decision by deleting the provisions above and replace them with the notice
  36.  * and other provisions required by the GPL or the LGPL. If you do not delete
  37.  * the provisions above, a recipient may use your version of this file under
  38.  * the terms of any one of the MPL, the GPL or the LGPL.
  39.  *
  40.  * ***** END LICENSE BLOCK ***** */
  41.  
  42. //
  43. // calItemBase.js
  44. //
  45.  
  46. const ICAL = Components.interfaces.calIIcalComponent;
  47. const kHashPropertyBagContractID = "@mozilla.org/hash-property-bag;1";
  48. const kIWritablePropertyBag = Components.interfaces.nsIWritablePropertyBag;
  49. const HashPropertyBag = new Components.Constructor(kHashPropertyBagContractID, kIWritablePropertyBag);
  50.  
  51. function NewCalDateTime(aJSDate) {
  52.     var c = new CalDateTime();
  53.     if (aJSDate)
  54.         c.jsDate = aJSDate;
  55.     return c;
  56. }
  57.  
  58. function calItemBase() {
  59.     this.mPropertyParams = {};
  60. }
  61.  
  62. calItemBase.prototype = {
  63.     mPropertyParams: null,
  64.     mIsProxy: false,
  65.  
  66.     QueryInterface: function (aIID) {
  67.         if (!aIID.equals(Components.interfaces.nsISupports) &&
  68.             !aIID.equals(Components.interfaces.calIItemBase))
  69.         {
  70.             throw Components.results.NS_ERROR_NO_INTERFACE;
  71.         }
  72.  
  73.         return this;
  74.     },
  75.  
  76.     mParentItem: null,
  77.     get parentItem() {
  78.         if (this.mParentItem)
  79.             return this.mParentItem;
  80.         else
  81.             return this;
  82.     },
  83.     set parentItem(value) {
  84.         if (this.mImmutable)
  85.             throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
  86.         this.mIsProxy = true;
  87.         this.mParentItem = value;
  88.     },
  89.  
  90.     initializeProxy: function (aParentItem) {
  91.         if (this.mImmutable)
  92.             throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
  93.  
  94.         if (this.mParentItem != null)
  95.             throw Components.results.NS_ERROR_FAILURE;
  96.  
  97.         this.mParentItem = aParentItem;
  98.         this.mCalendar = aParentItem.mCalendar;
  99.         this.mIsProxy = true;
  100.     },
  101.  
  102.     //
  103.     // calIItemBase
  104.     //
  105.     mImmutable: false,
  106.     get isMutable() { return !this.mImmutable; },
  107.  
  108.     mDirty: false,
  109.     modify: function() {
  110.         if (this.mImmutable)
  111.             throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
  112.         this.mDirty = true;
  113.     },
  114.  
  115.     ensureNotDirty: function() {
  116.         if (!this.mDirty)
  117.             return;
  118.  
  119.         if (this.mImmutable) {
  120.             dump ("### Something tried to undirty a dirty immutable event!\n");
  121.             throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
  122.         }
  123.  
  124.         this.setProperty("LAST-MODIFIED", NewCalDateTime(new Date()));
  125.         this.mDirty = false;
  126.     },
  127.  
  128.     makeItemBaseImmutable: function() {
  129.         if (this.mImmutable)
  130.             throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
  131.  
  132.         // make all our components immutable
  133.         if (this.mRecurrenceInfo)
  134.             this.mRecurrenceInfo.makeImmutable();
  135.  
  136.         if (this.mOrganizer)
  137.             this.mOrganizer.makeImmutable();
  138.         if (this.mAttendees) {
  139.             for (var i = 0; i < this.mAttendees.length; i++)
  140.                 this.mAttendees[i].makeImmutable();
  141.         }
  142.  
  143.         var e = this.mProperties.enumerator;
  144.         while (e.hasMoreElements()) {
  145.             var prop = e.getNext().QueryInterface(Components.interfaces.nsIProperty);
  146.             var val = prop.value;
  147.  
  148.             if (prop.value instanceof Components.interfaces.calIDateTime) {
  149.                 if (prop.value.isMutable)
  150.                     prop.value.makeImmutable();
  151.             }
  152.         }
  153.  
  154.         if (this.alarmOffset) {
  155.             this.alarmOffset.makeImmutable();
  156.             if (this.alarmLastAck) {
  157.                 this.alarmLastAck.makeImmutable();
  158.             }
  159.         }
  160.  
  161.         this.ensureNotDirty();
  162.         this.mImmutable = true;
  163.     },
  164.  
  165.     hasSameIds: function(that) {
  166.         return (that && this.id == that.id &&
  167.                 (this.recurrenceId == that.recurrenceId || // both null
  168.                  (this.recurrenceId && that.recurrenceId &&
  169.                   this.recurrenceId.compare(that.recurrenceId) == 0)));
  170.     },
  171.  
  172.     // initialize this class's members
  173.     initItemBase: function () {
  174.         var now = new Date();
  175.  
  176.         this.mProperties = new HashPropertyBag();
  177.  
  178.         this.setProperty("CREATED", NewCalDateTime(now));
  179.         this.setProperty("LAST-MODIFIED", NewCalDateTime(now));
  180.         this.setProperty("DTSTAMP", NewCalDateTime(now));
  181.  
  182.         this.mAttendees = null;
  183.  
  184.         this.mRecurrenceInfo = null;
  185.  
  186.         this.mAttachments = null;
  187.     },
  188.  
  189.     // for subclasses to use; copies the ItemBase's values
  190.     // into m. aNewParent is optional
  191.     cloneItemBaseInto: function (m, aNewParent) {
  192.         this.ensureNotDirty();
  193.  
  194.         m.mImmutable = false;
  195.         m.mIsProxy = this.mIsProxy;
  196.         m.mParentItem = aNewParent || this.mParentItem;
  197.  
  198.         m.mCalendar = this.mCalendar;
  199.         if (this.mRecurrenceInfo) {
  200.             m.mRecurrenceInfo = this.mRecurrenceInfo.clone();
  201.             m.mRecurrenceInfo.item = m;
  202.         }
  203.  
  204.         if (this.mOrganizer) {
  205.             m.mOrganizer = this.mOrganizer.clone();
  206.         }
  207.  
  208.         if (this.mAttendees) {
  209.             m.mAttendees = new Array(this.mAttendees.length);
  210.             for (var i = 0; i < this.mAttendees.length; i++)
  211.                 m.mAttendees[i] = this.mAttendees[i].clone();
  212.         }
  213.         else
  214.             m.mAttendees = null;
  215.  
  216.         m.mProperties = Components.classes["@mozilla.org/hash-property-bag;1"].
  217.                         createInstance(Components.interfaces.nsIWritablePropertyBag);
  218.  
  219.         var e = this.mProperties.enumerator;
  220.         while (e.hasMoreElements()) {
  221.             var prop = e.getNext().QueryInterface(Components.interfaces.nsIProperty);
  222.             var val = prop.value;
  223.  
  224.             if (prop.value instanceof Components.interfaces.calIDateTime)
  225.                 val = prop.value.clone();
  226.  
  227.             m.mProperties.setProperty (prop.name, val);
  228.         }
  229.  
  230.         m.mDirty = false;
  231.  
  232.         // these need fixing
  233.         m.mAttachments = this.mAttachments;
  234.  
  235.         // Clone any alarm info that exists, set it to null if it doesn't
  236.         if (this.alarmOffset) {
  237.             m.alarmOffset = this.alarmOffset.clone();
  238.             if (this.alarmLastAck) {
  239.                 m.alarmLastAck = this.alarmLastAck.clone();
  240.             } else {
  241.                 m.alarmLastAck = null;
  242.             }
  243.         } else {
  244.             m.alarmOffset = null;
  245.         }
  246.         m.alarmRelated = this.alarmRelated;
  247.  
  248.         return m;
  249.     },
  250.  
  251.     get lastModifiedTime() {
  252.         this.ensureNotDirty();
  253.         return this.getProperty("LAST-MODIFIED");
  254.     },
  255.  
  256.     get stampTime() {
  257.         var prop = this.getProperty("DTSTAMP");
  258.         if (prop && prop.isValid)
  259.             return prop;
  260.         return this.getProperty("LAST-MODIFIED");
  261.     },
  262.  
  263.     updateStampTime: function() {
  264.         // can't update the stamp time on an immutable event
  265.         if (this.mImmutable)
  266.             return;
  267.  
  268.         this.modify();
  269.         this.setProperty("DTSTAMP", NewCalDateTime(new Date()));
  270.     },
  271.  
  272.     get unproxiedPropertyEnumerator() {
  273.         return this.mProperties.enumerator;
  274.     },
  275.  
  276.     get propertyEnumerator() {
  277.         if (this.mIsProxy) {
  278.             // nsISimpleEnumerator sucks.  It really, really sucks.
  279.             // The interface is badly defined, it's not clear
  280.             // what happens if you just keep calling getNext() without
  281.             // calling hasMoreElements in between, which seems like more
  282.             // of an informational thing.  An interface with
  283.             // "advance()" which returns true or false, and with "item()",
  284.             // which returns the item the enumerator is pointing to, makes
  285.             // far more sense.  Right now we have getNext() doing both
  286.             // item returning and enumerator advancing, which makes
  287.             // no sense.
  288.             return {
  289.                 firstEnumerator: this.mProperties.enumerator,
  290.                 secondEnumerator: this.mParentItem.propertyEnumerator,
  291.                 handledProperties: { },
  292.  
  293.                 currentItem: null,
  294.  
  295.                 QueryInterface: function(aIID) {
  296.                     if (!aIID.equals(Components.interfaces.nsISimpleEnumerator) ||
  297.                         !aIID.equals(Components.interfaces.nsISupports))
  298.                     {
  299.                         throw Components.results.NS_ERROR_NO_INTERFACE;
  300.                     }
  301.                     return this;
  302.                 },
  303.  
  304.                 hasMoreElements: function() {
  305.                     if (!this.secondEnumerator)
  306.                         return false;
  307.  
  308.                     if (this.firstEnumerator) {
  309.                         var moreFirst = this.firstEnumerator.hasMoreElements();
  310.                         if (moreFirst) {
  311.                             this.currentItem = this.firstEnumerator.getNext();
  312.                             this.handledProperties[this.currentItem.name] = true;
  313.                             return true;
  314.                         }
  315.                         this.firstEnumerator = null;
  316.                     }
  317.  
  318.                     var moreSecond = this.secondEnumerator.hasMoreElements();
  319.                     if (moreSecond) {
  320.                         while (this.currentItem.name in this.handledProperties &&
  321.                                this.secondEnumerator.hasMoreElements())
  322.                         do {
  323.                             this.currentItem = this.secondEnumerator.getNext();
  324.                         } while (this.currentItem.name in this.handledProperties &&
  325.                                  ((this.currentItem = null) == null) && // hack
  326.                                  this.secondEnumerator.hasMoreElements());
  327.  
  328.                         if (!this.currentItem)
  329.                             return false;
  330.  
  331.                         return true;
  332.                     }
  333.  
  334.                     this.secondEnumerator = null;
  335.  
  336.                     return false;
  337.                 },
  338.  
  339.                 getNext: function() {
  340.                     if (!this.currentItem)
  341.                         throw Components.results.NS_ERROR_UNEXPECTED;
  342.  
  343.                     var rval = this.currentItem;
  344.                     this.currentItem = null;
  345.                     return rval;
  346.                 }
  347.             };
  348.         } else {
  349.             return this.mProperties.enumerator;
  350.         }
  351.     },
  352.  
  353.     // The has/get/getUnproxied/set/deleteProperty methods are case-insensitive.
  354.     getProperty: function (aName) {
  355.         aName = aName.toUpperCase();
  356.         try {
  357.             return this.mProperties.getProperty(aName);
  358.         } catch (e) {
  359.             try {
  360.                 if (this.mIsProxy) {
  361.                     return this.mParentItem.getProperty(aName);
  362.                 }
  363.             } catch (e) {}
  364.  
  365.             return null;
  366.         }
  367.     },
  368.  
  369.     getUnproxiedProperty: function (aName) {
  370.         try {
  371.             return this.mProperties.getProperty(aName.toUpperCase());
  372.         } catch (e) { }
  373.         return null;
  374.     },
  375.  
  376.     hasProperty: function (aName) {
  377.         return (this.getProperty(aName.toUpperCase()) != null);
  378.     },
  379.  
  380.     setProperty: function (aName, aValue) {
  381.         if (aName == "LAST-MODIFIED") {
  382.             this.mDirty = false;
  383.         } else {
  384.             this.modify();
  385.         }
  386.         this.mProperties.setProperty(aName.toUpperCase(), aValue);
  387.     },
  388.  
  389.     deleteProperty: function (aName) {
  390.         this.modify();
  391.         try {
  392.             this.mProperties.deleteProperty(aName.toUpperCase());
  393.         } catch (e) { }
  394.     },
  395.  
  396.     getPropertyParameter: function getPP(aPropName, aParamName) {
  397.         return this.mPropertyParams[aPropName][aParamName];
  398.     },
  399.  
  400.     getAttendees: function (countObj) {
  401.         if (!this.mAttendees && this.mIsProxy && this.mParentItem) {
  402.             this.mAttendees = this.mParentItem.getAttendees(countObj);
  403.         }
  404.         if (this.mAttendees) {
  405.             countObj.value = this.mAttendees.length;
  406.             return this.mAttendees.concat([]); // clone
  407.         }
  408.         else {
  409.             countObj.value = 0;
  410.             return [];
  411.         }
  412.     },
  413.  
  414.     getAttendeeById: function (id) {
  415.         var attendees = this.getAttendees({});
  416.         var lowerCaseId = id.toLowerCase();
  417.         for each (var attendee in attendees) {
  418.             // This match must be case insensitive to deal with differing
  419.             // cases of things like MAILTO:
  420.             if (attendee.id.toLowerCase() == lowerCaseId) {
  421.                 return attendee;
  422.             }
  423.         }
  424.         return null;
  425.     },
  426.  
  427.     removeAttendee: function (attendee) {
  428.         this.modify();
  429.         var found = false, newAttendees = [];
  430.         var attendees = this.getAttendees({});
  431.         var attIdLowerCase =attendee.id.toLowerCase();
  432.  
  433.         for (var i = 0; i < attendees.length; i++) {
  434.             if (attendees[i].id.toLowerCase() != attIdLowerCase)
  435.                 newAttendees.push(attendees[i]);
  436.             else
  437.                 found = true;
  438.         }
  439.         if (found)
  440.             this.mAttendees = newAttendees;
  441.         else
  442.             throw Component.results.NS_ERROR_INVALID_ARG;
  443.     },
  444.  
  445.     removeAllAttendees: function() {
  446.         this.modify();
  447.         this.mAttendees = [];
  448.     },
  449.  
  450.     addAttendee: function (attendee) {
  451.         this.modify();
  452.         this.mAttendees = this.getAttendees({});
  453.         this.mAttendees.push(attendee);
  454.     },
  455.  
  456.     mCalendar: null,
  457.     get calendar () {
  458.         return this.mCalendar;
  459.     },
  460.  
  461.     set calendar (v) {
  462.         if (this.mImmutable)
  463.             throw Components.results.NS_ERROR_OBJECT_IS_IMMUTABLE;
  464.         this.mCalendar = v;
  465.     },
  466.  
  467.     mOrganizer: null,
  468.     get organizer() {
  469.         if (!this.mOrganizer && this.mIsProxy && this.mParentItem) {
  470.             return this.mParentItem.organizer;
  471.         }
  472.         else
  473.             return this.mOrganizer;
  474.     },
  475.  
  476.     set organizer(v) {
  477.         this.modify();
  478.         this.mOrganizer = v;
  479.     },
  480.  
  481.     /* MEMBER_ATTR(mIcalString, "", icalString), */
  482.     get icalString() {
  483.         throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  484.     },
  485.  
  486.     set icalString() {
  487.         throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  488.     },
  489.  
  490.     // All of these property names must be in upper case for isPropertyPromoted to
  491.     // function correctly. The has/get/getUnproxied/set/deleteProperty interfaces
  492.     // are case-insensitive, but these are not.
  493.     itemBasePromotedProps: {
  494.         "CREATED": true,
  495.         "UID": true,
  496.         "LAST-MODIFIED": true,
  497.         "SUMMARY": true,
  498.         "PRIORITY": true,
  499.         "METHOD": true,
  500.         "STATUS": true,
  501.         "CLASS": true,
  502.         "DTSTAMP": true,
  503.         "X-MOZILLA-GENERATION": true,
  504.         "RRULE": true,
  505.         "EXDATE": true,
  506.         "RDATE": true,
  507.         "ATTENDEE": true,
  508.         "ORGANIZER": true,
  509.         "RECURRENCE-ID": true
  510.     },
  511.  
  512.     icsBasePropMap: [
  513.     { cal: "CREATED", ics: "createdTime" },
  514.     { cal: "LAST-MODIFIED", ics: "lastModified" },
  515.     { cal: "DTSTAMP", ics: "stampTime" },
  516.     { cal: "UID", ics: "uid" },
  517.     { cal: "SUMMARY", ics: "summary" },
  518.     { cal: "PRIORITY", ics: "priority" },
  519.     { cal: "STATUS", ics: "status" },
  520.     { cal: "CLASS", ics: "icalClass" },
  521.     { cal: "RECURRENCE-ID", ics: "recurrenceId" } ],
  522.  
  523.     mapPropsFromICS: function(icalcomp, propmap) {
  524.         for (var i = 0; i < propmap.length; i++) {
  525.             var prop = propmap[i];
  526.             var val = icalcomp[prop.ics];
  527.             if (val != null && val != ICAL.INVALID_VALUE)
  528.                 this.setProperty(prop.cal, val);
  529.         }
  530.     },
  531.  
  532.     mapPropsToICS: function(icalcomp, propmap) {
  533.         for (var i = 0; i < propmap.length; i++) {
  534.             var prop = propmap[i];
  535.             var val = this.getProperty(prop.cal);
  536.             if (val != null && val != ICAL.INVALID_VALUE)
  537.                 icalcomp[prop.ics] = val;
  538.         }
  539.     },
  540.  
  541.     setItemBaseFromICS: function (icalcomp) {
  542.         this.modify();
  543.  
  544.         this.mapPropsFromICS(icalcomp, this.icsBasePropMap);
  545.  
  546.         for (var attprop = icalcomp.getFirstProperty("ATTENDEE");
  547.              attprop;
  548.              attprop = icalcomp.getNextProperty("ATTENDEE")) {
  549.             
  550.             var att = new CalAttendee();
  551.             att.icalProperty = attprop;
  552.             this.addAttendee(att);
  553.         }
  554.  
  555.         var orgprop = icalcomp.getFirstProperty("ORGANIZER");
  556.         if (orgprop) {
  557.             var org = new CalAttendee();
  558.             org.icalProperty = orgprop;
  559.             org.isOrganizer = true;
  560.             this.mOrganizer = org;
  561.         }
  562.         
  563.         var gen = icalcomp.getFirstProperty("X-MOZILLA-GENERATION");
  564.         if (gen)
  565.             this.mGeneration = parseInt(gen.value);
  566.  
  567.         // find recurrence properties
  568.         var rec = null;
  569.         for (var recprop = icalcomp.getFirstProperty("ANY");
  570.              recprop;
  571.              recprop = icalcomp.getNextProperty("ANY"))
  572.         {
  573.             var ritem = null;
  574.             if (recprop.propertyName == "RRULE" ||
  575.                 recprop.propertyName == "EXRULE")
  576.             {
  577.                 ritem = new CalRecurrenceRule();
  578.             } else if (recprop.propertyName == "RDATE" ||
  579.                        recprop.propertyName == "EXDATE")
  580.             {
  581.                 ritem = new CalRecurrenceDate();
  582.             } else {
  583.                 continue;
  584.             }
  585.  
  586.             ritem.icalProperty = recprop;
  587.  
  588.             if (!rec) {
  589.                 rec = new CalRecurrenceInfo();
  590.                 rec.item = this;
  591.             }
  592.  
  593.             rec.appendRecurrenceItem(ritem);
  594.         }
  595.         this.mRecurrenceInfo = rec;
  596.  
  597.         var alarmComp = icalcomp.getFirstSubcomponent("VALARM");
  598.         if (alarmComp) {
  599.             var triggerProp = alarmComp.getFirstProperty("TRIGGER");
  600.             // Really, really old Sunbird/Calendar versions didn't give us a
  601.             // trigger.
  602.             if (!triggerProp) {
  603.                 Components.utils.reportError("No trigger property for alarm on item: "+this.id);
  604.                 // No parsing happens after alarms, so just return
  605.                 return;
  606.             }
  607.             var duration = Components.classes["@mozilla.org/calendar/duration;1"]
  608.                                      .createInstance(Components.interfaces.calIDuration);
  609.             duration.icalString = triggerProp.valueAsIcalString;
  610.             this.alarmOffset = duration;
  611.  
  612.             var related = triggerProp.getParameter("RELATED");
  613.             if (related && related == "END")
  614.                 this.alarmRelated = Components.interfaces.calIItemBase.ALARM_RELATED_END;
  615.             else
  616.                 this.alarmRelated = Components.interfaces.calIItemBase.ALARM_RELATED_START;
  617.  
  618.             var lastAck = alarmComp.getFirstProperty("X-MOZ-LASTACK");
  619.             if (lastAck) {
  620.                 var lastAckTime = Components.classes["@mozilla.org/calendar/datetime;1"]
  621.                                             .createInstance(Components.interfaces.calIDateTime);
  622.                 lastAckTime.icalString = lastAck.valueAsIcalString;
  623.                 this.alarmLastAck = lastAckTime;
  624.             }
  625.  
  626.             var email = alarmComp.getFirstProperty("X-EMAILADDRESS");
  627.             if (email)
  628.                 this.setProperty("alarmEmailAddress", email.value);
  629.         }
  630.     },
  631.  
  632.     importUnpromotedProperties: function (icalcomp, promoted) {
  633.         for (var prop = icalcomp.getFirstProperty("ANY");
  634.              prop;
  635.              prop = icalcomp.getNextProperty("ANY")) {
  636.             if (!promoted[prop.propertyName]) {
  637.                 this.setProperty(prop.propertyName, prop.value);
  638.                 var param = prop.getFirstParameterName();
  639.                 while (param) {
  640.                     if (!(prop.propertyName in this.mPropertyParams)) {
  641.                         this.mPropertyParams[prop.propertyName] = {};
  642.                     }
  643.                     this.mPropertyParams[prop.propertyName][param] = prop.getParameter(param);
  644.                     param = prop.getNextParameterName();
  645.                 }
  646.             }
  647.         }
  648.     },
  649.  
  650.     // This method is case-insensitive.
  651.     isPropertyPromoted: function (name) {
  652.         return (this.itemBasePromotedProps[name.toUpperCase()]);
  653.     },
  654.  
  655.     get icalComponent() {
  656.         throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  657.     },
  658.  
  659.     fillIcalComponentFromBase: function (icalcomp) {
  660.         // Make sure that the LMT and ST are updated
  661.         this.updateStampTime();
  662.         this.ensureNotDirty();
  663.  
  664.         this.mapPropsToICS(icalcomp, this.icsBasePropMap);
  665.  
  666.         if (this.mOrganizer)
  667.             icalcomp.addProperty(this.mOrganizer.icalProperty);
  668.         var attendees = this.getAttendees({});
  669.         if (attendees.length > 0) {
  670.           for (var i = 0; i < attendees.length; i++) {
  671.             icalcomp.addProperty(attendees[i].icalProperty);
  672.           }
  673.         }
  674.  
  675.         if (this.mGeneration) {
  676.             var genprop = icalProp("X-MOZILLA-GENERATION");
  677.             genprop.value = String(this.mGeneration);
  678.             icalcomp.addProperty(genprop);
  679.         }
  680.  
  681.         if (this.mRecurrenceInfo) {
  682.             var ritems = this.mRecurrenceInfo.getRecurrenceItems({});
  683.             for (i in ritems) {
  684.                 icalcomp.addProperty(ritems[i].icalProperty);
  685.             }
  686.         }
  687.         
  688.         if (this.alarmOffset) {
  689.             const icssvc = Components.classes["@mozilla.org/calendar/ics-service;1"]
  690.                                      .getService(Components.interfaces.calIICSService);
  691.             var alarmComp = icssvc.createIcalComponent("VALARM");
  692.  
  693.             var triggerProp = icssvc.createIcalProperty("TRIGGER");
  694.             triggerProp.valueAsIcalString = this.alarmOffset.icalString;
  695.  
  696.             if (this.alarmRelated == Components.interfaces.calIItemBase.ALARM_RELATED_END)
  697.                 triggerProp.setParameter("RELATED", "END");
  698.  
  699.             alarmComp.addProperty(triggerProp);
  700.  
  701.             if (this.alarmLastAck) {
  702.                 var lastAck = icssvc.createIcalProperty("X-MOZ-LASTACK");
  703.                 lastAck.valueAsIcalString = this.alarmLastAck.icalString;
  704.                 alarmComp.addProperty(lastAck);
  705.             }
  706.  
  707.             // We don't use this, but the ics-spec requires it
  708.             var descProp = icssvc.createIcalProperty("DESCRIPTION");
  709.             descProp.value = "Mozilla Alarm: "+ this.title;
  710.             alarmComp.addProperty(descProp);
  711.  
  712.             var actionProp = icssvc.createIcalProperty("ACTION");
  713.             actionProp.value = "DISPLAY";
  714.  
  715.             if (this.getProperty("alarmEmailAddress")) {
  716.                 var emailProp = icssvc.createIcalProperty("X-EMAILADDRESS");
  717.                 emailProp.value = this.getProperty("alarmEmailAddress");
  718.                 actionProp.value = "EMAIL";
  719.                 alarmComp.addProperty(emailProp);
  720.             }
  721.  
  722.             alarmComp.addProperty(actionProp);
  723.  
  724.             icalcomp.addSubcomponent(alarmComp);
  725.         }
  726.     },
  727.     
  728.     getOccurrencesBetween: function(aStartDate, aEndDate, aCount) {
  729.         throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  730.     }
  731. };
  732.  
  733. makeMemberAttr(calItemBase, "X-MOZILLA-GENERATION", 0, "generation", true);
  734. makeMemberAttr(calItemBase, "CREATED", null, "creationDate", true);
  735. makeMemberAttr(calItemBase, "UID", null, "id", true);
  736. makeMemberAttr(calItemBase, "SUMMARY", null, "title", true);
  737. makeMemberAttr(calItemBase, "PRIORITY", 0, "priority", true);
  738. makeMemberAttr(calItemBase, "CLASS", "PUBLIC", "privacy", true);
  739. makeMemberAttr(calItemBase, "STATUS", null, "status", true);
  740. makeMemberAttr(calItemBase, "ALARMTIME", null, "alarmTime", true);
  741. makeMemberAttr(calItemBase, "RECURRENCE-ID", null, "recurrenceId", true);
  742.  
  743. makeMemberAttr(calItemBase, "mRecurrenceInfo", null, "recurrenceInfo");
  744. makeMemberAttr(calItemBase, "mAttachments", null, "attachments");
  745. makeMemberAttr(calItemBase, "mProperties", null, "properties");
  746.  
  747. function makeMemberAttr(ctor, varname, dflt, attr, asProperty)
  748. {
  749.     // XXX handle defaults!
  750.     var getter = function () {
  751.         if (asProperty)
  752.             return this.getProperty(varname);
  753.         else
  754.             return this[varname];
  755.     };
  756.     var setter = function (v) {
  757.         this.modify();
  758.         if (asProperty)
  759.             this.setProperty(varname, v);
  760.         else
  761.             this[varname] = v;
  762.     };
  763.     ctor.prototype.__defineGetter__(attr, getter);
  764.     ctor.prototype.__defineSetter__(attr, setter);
  765. }
  766.  
  767. //
  768. // helper functions
  769. //
  770.  
  771. function icalFromString(str)
  772. {
  773.     const icssvc = Components.classes["@mozilla.org/calendar/ics-service;1"].
  774.         getService(Components.interfaces.calIICSService);
  775.     return icssvc.parseICS(str);
  776. }
  777.  
  778. function icalProp(kind)
  779. {
  780.     const icssvc = Components.classes["@mozilla.org/calendar/ics-service;1"].
  781.         getService(Components.interfaces.calIICSService);
  782.     return icssvc.createIcalProperty(kind);
  783. }
  784.